#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include <sstream>
#include <fstream>
#include <functional>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "Log.hpp"
using namespace std;

static const string sep = "\r\n";            // 整体报文分隔符
static const string header_sep = ": ";       // 请求报头kv字符串分隔符
static const string webroot = "wwwroot";     // url拼接保存客户端访问的资源所在的目录
static const string homepage = "index.html"; // url拼接站点首页

static const string httpversion = "HTTP/1.0";
static const string space = " ";
static const string filesuffixsep = ".";
static const string argsep = "?";

class HttpRequest;
class HttpResponse;
using func_t= function < shared_ptr<HttpResponse> (shared_ptr<HttpRequest>)>;

class HttpRequest
{
private:
    string GetOneLine(string &reqstr)
    {
        if (reqstr.empty())
            return string();
        size_t pos = reqstr.find(sep, 0);
        if (pos == string::npos)
            return string();
        string line = reqstr.substr(0, pos);
        reqstr.erase(0, pos + sep.size()); // 从字符串开始位置去除一行包括换行符"\r\n"
        // 提取字符串时出现空行情形: 请求报头\r\n\r\n正文部分 --->\r\n正文部分
        //  string line=reqstr.substr(0,0)
        if (line.empty())
            return sep;
        return line;
    }

    bool ParseHeaderHelper(string &line, string *key, string *value)
    {
        if (line.empty())
            return false;
        size_t pos = line.find(header_sep);
        if (pos == string::npos)
            return false;
        *key = line.substr(0, pos);
        *value = line.substr(pos + header_sep.size());
        return true;
    }

public:
    HttpRequest()
        : _blank_line(sep), _path(webroot)
    {
    }

    bool ParseReqLine()
    {
        if (_req_line.empty())
            return false;
        stringstream ss(_req_line);

        ss >> _req_method >> _url >> _version;
        // url: /index.html?user=zhangsan&passwd=12345678
        // GET方法以url形式提交的参数所以添加类成员变量_args接收参数
        if (strcasecmp(_req_method.c_str(), "GET") == 0)
        {
            size_t pos = _url.find(argsep);
            if (pos != string::npos)
            {
                LOG(INFO, "change begin,url:%s", _url.c_str());
                _args = _url.substr(pos + argsep.size()); // user=zhangsan&passwd=12345678
                _url.erase(pos);                          // /index.html
                LOG(INFO, "change done,_url:%s,_args:%s", _url.c_str(), _args.c_str());
            }
        }

        //  wwwroot/  wwwroot/index.html
        _path += _url;
        if (_path[_path.size() - 1] == '/')
        {
            _path += homepage;
        }

        size_t pos = _path.rfind(filesuffixsep);
        if (pos == string::npos)
        {
            _suffix = ".unknown";
        }
        else
        {
            _suffix = _path.substr(pos); //".html"
        }
        LOG(DEBUG, "client want get %s", _path.c_str());
        return true;
    }
    void ParseReqHeader()
    {
        // vector中每个元素格式信息:Connection: keep-alive
        // 不包括换行符"\r\n"
        for (auto &header : _req_header)
        {
            string key, value;
            if (ParseHeaderHelper(header, &key, &value))
            {
                _headers.insert(make_pair(key, value));
            }
        }
    }

    void Deserialize(string &reqstr)
    {
        _req_line = GetOneLine(reqstr); // 提取请求行
        // 提取请求报头
        while (true)
        {
            string line = GetOneLine(reqstr);
            if (line.empty())
            {
                // 提取出错
                break;
            }

            else if (line == sep)
            {
                // 提取到空行&&空行已经被删除则剩余部分为正文
                // POST方法以请求正文的形式提交的参数
                _req_text = reqstr;
                break;
            }
            else
            {
                // 提取请求报头之后&&空行之前的请求报头
                _req_header.push_back(line);
            }
        }
        // 请求行二次解析
        ParseReqLine();
        // 请求报头二次解析
        ParseReqHeader();
    }

    string Path()
    {
        return _path;
    }

    string Suffix()
    {
        return _suffix;
    }

    // 请求的正文部分_req_text不为空或者_args不为空说明请求时携带参数
    bool IsExec()
    {
        return !_args.empty() || !_req_text.empty();
    }

    // GET方法以url形式提交的参数保存_args,向类外提供参数,方便上层处理参数
    string Args()
    {
        return _args;
    }
    // POST方法以请求正文的形式提交的参数保存_req_text,提供接口方便上层处理
    string Text()
    {
        return _req_text;
    }

    //提供上层方便判定是GET方法提交的参数还是POST方法
    string Method()
    {
        return _req_method;
    }

    // void Print()
    // {
    //     cout << "====" << _req_line << endl;
    //     for (auto &header : _req_header)
    //     {
    //         cout << "####" << header << endl;
    //     }
    //     cout << _blank_line;
    //     cout << "@@@@" << _req_text << endl;

    //     cout << "method ****" << _req_method << endl;
    //     cout << "url ^^^^" << _url << endl;
    //     cout << "path ####" << _path << endl;
    //     cout << "version &&&&" << _version << endl;
    //     for (auto &header : _headers)
    //     {
    //         cout << "[" << header.first << ":" << header.second << "]" << endl;
    //     }
    // }

    ~HttpRequest()
    {
    }

private:
    // 1.按照原始协议内容初次解析
    string _req_line;           // 请求行
    vector<string> _req_header; // 请求报头
    string _blank_line;         // 空行
    string _req_text;           // 请求正文

    // 2.按照期望的解析结果二次解析
    string _req_method; // 请求方法
    string _url;        // web根目录
    string _suffix;     // wwwroot/1.html 文件后缀

    string _args; // GET/POST方法中携带的参数

    string _version;                        // http版本
    unordered_map<string, string> _headers; // 请求报头中key-Value关系

    string _path; // 资源的保存路径
};

class HttpResponse
{
public:
    HttpResponse()
        : _blank_line(sep), _version(httpversion)
    {
    }

    // 1.添加状态行
    void AddStatusLine(int code, const string &desc)
    {
        _code = code;
        _desc = desc;
    }

    // 2.添加响应报头
    void AddHeader(const string &key, const string &value)
    {
        _headers[key] = value;
    }

    // 3.添加响应正文
    void AddText(const string &text)
    {
        _resp_text = text;
    }

    string Serialize()
    {
        // 状态行序列化
        string _status_line = _version + space + to_string(_code) + space + _desc + sep;
        // 响应报头序列化即填充_resp_header
        for (auto &header : _headers)
        {
            string respheader = header.first + header_sep + header.second + sep;
            _resp_header.push_back(respheader);
        }

        // 序列化
        string respstr = _status_line;
        for (auto &header : _resp_header)
        {
            respstr += header;
        }
        respstr += _blank_line;
        respstr += _resp_text;
        return respstr;
    }

    ~HttpResponse()
    {
    }

private:
    // 构建应答必要字段
    string _version;                        // 应答http版本
    int _code;                              // 应答状态码
    string _desc;                           // 应答状态码描述
    unordered_map<string, string> _headers; // 响应报头的结构化数据

    // 应答的结构化字段
    string _resp_line;           // 状态行
    vector<string> _resp_header; // 响应报头
    string _blank_line;          // 空行
    string _resp_text;           // 响应正文
};

class Factory
{
public:
    static shared_ptr<HttpRequest> BuildRequest()
    {
        return make_shared<HttpRequest>();
    }

    static shared_ptr<HttpResponse> BuildResponse()
    {
        return make_shared<HttpResponse>();
    }
};

class HttpServer
{
public:
    HttpServer()
    {
        // Content-Type对照表
        _mime_type.insert(make_pair(".html", "text/html"));
        _mime_type.insert(make_pair(".css", "text/css"));
        _mime_type.insert(make_pair(".js", "application/x-javascript"));
        _mime_type.insert(make_pair(".png", "image/png"));
        _mime_type.insert(make_pair(".jpg", "image/jpeg"));
        _mime_type.insert(make_pair(".unknown", "text/html"));

        // 状态码描述表
        _code_to_desc.insert(make_pair(100, "Continue"));
        _code_to_desc.insert(make_pair(200, "OK"));
        _code_to_desc.insert(make_pair(301, "Moved Permanently")); // 永久重定向
        _code_to_desc.insert(make_pair(302, "Found"));             // 临时重定向
        _code_to_desc.insert(make_pair(404, "Not Found"));         // 404页面
        _code_to_desc.insert(make_pair(500, "Internal Server Error"));
    }

    void AddHandler(const string pathname, func_t func)
    {
        // wwwroot/login
        string key=webroot+pathname;
        _funcs[key] = func;
    }

    // 读取_path路径下指定文件,必须按照二进制形式打开文件(文件可能是图片,视频,...)
    string ReadFileContent(const string &path, int *size)
    {
        ifstream in(path, ios::binary | ios::in);
        if (!in.is_open())
            return string();

        // 获取文件大小
        in.seekg(0, ios::end);
        int filesize = in.tellg();
        in.seekg(0, ios::beg);

        string content;
        content.resize(filesize);
        in.read((char *)content.c_str(), filesize);

        in.close();

        *size = filesize;
        return content;
    }

    // #define TEST
    string HandlerHttpRequest(string request)
    {
#ifdef TEST
        std::cout << "---------------------------------------" << std::endl;
        std::cout << request;

        std::string response = "HTTP/1.0 200 OK\r\n"; // 404 NOT Found
        response += "\r\n";
        response += "<html><body><h1>Hello Linux!</h1></body></html>";
        return response;
#else
        std::cout << "---------------------------------------" << std::endl;
        std::cout << request;

        // 假定req为完整的Http请求
        // 1.将完整的http请求反序列化
        shared_ptr<HttpRequest> req = Factory::BuildRequest();
        req->Deserialize(request);
        // req->Print();
        // 2.业务处理,获取响应正文
        shared_ptr<HttpResponse> resp = Factory::BuildResponse();
        int code = 0;
        if (req->Path() == "wwwroot/redir")
        {
            code = 302;
            resp->AddStatusLine(code, _code_to_desc[code]);
            resp->AddHeader("Location", "https://www.baidu.com/"); // 站间跳转
            // resp->AddHeader("Location", "http://123.249.39.44:8899/image/1.jpeg");//站内跳转
            // resp->AddHeader("Location", "https://123.249.39.44:8889/3.html");
        }

        // 请求的正文部分_req_text不为空或者_args不为空说明请求时携带参数
        if (req->IsExec())
        {
            //key:req->Path() value: function func_t < shared_ptr<HttpResponse> (shared_ptr<HttpRequest>)>;
            //value为func_t函数,参数为shared_ptr<HttpRequest>,返回值为shared_ptr<HttpResponse>;
            shared_ptr<HttpResponse> response=_funcs[req->Path()](req);
            string responstr=response->Serialize();
            return responstr;
        }
        else
        {
            string resource = req->Path();
            int filesize;
            string text = ReadFileContent(resource, &filesize);
            if (text.empty())
            {
                code = 404;
                resp->AddStatusLine(code, _code_to_desc[code]);
                string text404 = ReadFileContent("wwwroot/404.html", &filesize);
                resp->AddHeader("Content-Length", to_string(filesize));
                resp->AddText(text404);
            }
            else
            {
                // 3.1 构建响应行
                code = 200;
                resp->AddStatusLine(code, _code_to_desc[code]);
                // 3.2 构建响应报文
                resp->AddHeader("Content-Length", to_string(filesize));
                // 3.3 构建响应正文
                resp->AddText(text);
                // 关键:如何得知状态码code为多少？
                string suffix = req->Suffix();
                resp->AddHeader("Content-Type", _mime_type[suffix]); // 数据类型表示为网页,则为text/html
            }
            // 4. 响应序列化
            string responsestr = resp->Serialize();
            // 5. 返回上层,发送数据
            return responsestr;
        }

#endif
    }

    ~HttpServer()
    {
    }

private:
    unordered_map<string, string> _mime_type; // Content-Type对照表;
    unordered_map<int, string> _code_to_desc; // 状态码描述表

    unordered_map<string, func_t> _funcs; 
    //string接收网页指定参数路径进行拼接,参数由HttpRequest进行分离,交由上层处理
    //将网页指定参数路径与回调函数(功能模块)建立映射关系
};